home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / logdaemon-2 / telnetd / telnetd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-31  |  30.1 KB  |  1,512 lines

  1. /*
  2.  * Copyright (c) 1983, 1986 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)telnetd.c    5.31 (Berkeley) 2/23/89";
  26. #endif /* not lint */
  27.  
  28. /*
  29.  * Telnet server.
  30.  */
  31. #include <sys/param.h>
  32. #include <sys/socket.h>
  33. #include <sys/wait.h>
  34. #include <sys/file.h>
  35. #include <sys/stat.h>
  36. #include <sys/time.h>
  37.  
  38. #include <netinet/in.h>
  39.  
  40. #include <arpa/telnet.h>
  41.  
  42. #include <stdio.h>
  43. #include <signal.h>
  44. #include <errno.h>
  45. #include <sgtty.h>
  46. #include <netdb.h>
  47. #include <syslog.h>
  48. #include <ctype.h>
  49.  
  50. #ifdef STREAM_PTY
  51. #include <sys/stropts.h>
  52. #include "../lib/tiocpkt.h"
  53. extern    char    *ptsname();
  54. #define PTY_PKT_READ    pty_pkt_read
  55. #else
  56. #define PTY_PKT_READ    read
  57. #endif
  58.  
  59. #ifndef O_RDWR
  60. #include <sys/fcntl.h>
  61. #endif
  62.  
  63. #ifndef FIONBIO
  64. #include <sys/filio.h>
  65. #endif
  66.  
  67. #ifndef TIOCSLTC
  68. #include <sys/ttold.h>
  69. #endif
  70.  
  71. #ifdef USE_STRING_H
  72. #include <string.h>
  73. #define index        strchr
  74. #define rindex        strrchr
  75. #define bcopy(s,d,l)    memcpy(d,s,l)
  76. #else
  77. #include <strings.h>
  78. #endif
  79.  
  80. #ifdef __STDC__
  81. #define puts        xputs        /* prototype conflict */
  82. #endif
  83.  
  84. /* Ultrix syslog(3) has no facility stuff. */
  85. #ifndef LOG_DAEMON
  86. #define LOG_DAEMON    0
  87. #define LOG_ODELAY    0
  88. #endif
  89.  
  90. #define    OPT_NO            0        /* won't do this option */
  91. #define    OPT_YES            1        /* will do this option */
  92. #define    OPT_YES_BUT_ALWAYS_LOOK    2
  93. #define    OPT_NO_BUT_ALWAYS_LOOK    3
  94. char    hisopts[256];
  95. char    myopts[256];
  96.  
  97. char    doopt[] = { IAC, DO, '%', 'c', 0 };
  98. char    dont[] = { IAC, DONT, '%', 'c', 0 };
  99. char    will[] = { IAC, WILL, '%', 'c', 0 };
  100. char    wont[] = { IAC, WONT, '%', 'c', 0 };
  101.  
  102. /*
  103.  * I/O data buffers, pointers, and counters.
  104.  */
  105. char    ptyibuf[BUFSIZ], *ptyip = ptyibuf;
  106.  
  107. char    ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
  108.  
  109. char    netibuf[BUFSIZ], *netip = netibuf;
  110. #define    NIACCUM(c)    {   *netip++ = c; \
  111.                 ncc++; \
  112.             }
  113.  
  114. char    netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
  115. char    *neturg = 0;        /* one past last bye of urgent data */
  116.     /* the remote system seems to NOT be an old 4.2 */
  117. int    not42 = 1;
  118.  
  119. #ifdef SUNOS5
  120. #define    BANNER    "\r\n\r\nUNIX(r) System V Release 4.0 (%s)\r\n\r\r\n\r"
  121. #endif
  122.  
  123. #ifdef SUNOS4
  124. #define    BANNER    "\r\n\r\nSunOS UNIX (%s)\r\n\r\r\n\r"
  125. #endif
  126.  
  127. #ifdef ultrix
  128. #define    BANNER  "\r\n\r\nULTRIX V4.2A (Rev. 47) (%s)\r\n\r\r\n\r"
  129. #endif
  130.  
  131. #ifndef BANNER
  132. #define    BANNER    "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
  133. #endif
  134.  
  135.         /* buffer for sub-options */
  136. char    subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
  137. #define    SB_CLEAR()    subpointer = subbuffer;
  138. #define    SB_TERM()    { subend = subpointer; SB_CLEAR(); }
  139. #define    SB_ACCUM(c)    if (subpointer < (subbuffer+sizeof subbuffer)) { \
  140.                 *subpointer++ = (c); \
  141.             }
  142. #define    SB_GET()    ((*subpointer++)&0xff)
  143. #define    SB_EOF()    (subpointer >= subend)
  144.  
  145. int    pcc, ncc;
  146.  
  147. int    pty, net;
  148. #ifdef STREAM_PTY
  149. int    pts;
  150. #endif
  151. int    inter;
  152. extern    char **environ;
  153. extern    int errno;
  154. char    *line;
  155. int    SYNCHing = 0;        /* we are in TELNET SYNCH mode */
  156. /*
  157.  * The following are some clocks used to decide how to interpret
  158.  * the relationship between various variables.
  159.  */
  160.  
  161. struct {
  162.     int
  163.     system,            /* what the current time is */
  164.     echotoggle,        /* last time user entered echo character */
  165.     modenegotiated,        /* last time operating mode negotiated */
  166.     didnetreceive,        /* last time we read data from network */
  167.     ttypeopt,        /* ttype will/won't received */
  168.     ttypesubopt,        /* ttype subopt is received */
  169.     getterminal,        /* time started to get terminal information */
  170.     gotDM;            /* when did we last see a data mark */
  171. } clocks;
  172.  
  173. #define    settimer(x)    (clocks.x = ++clocks.system)
  174. #define    sequenceIs(x,y)    (clocks.x < clocks.y)
  175.  
  176. main(argc, argv)
  177.     char *argv[];
  178. {
  179.     struct sockaddr_in from;
  180.     int on = 1, fromlen;
  181.  
  182. #if    defined(DEBUG)
  183.     {
  184.         int s, ns, foo;
  185.         struct servent *sp;
  186.         static struct sockaddr_in sin = { AF_INET };
  187.  
  188.         sp = getservbyname("telnet", "tcp");
  189.         if (sp == 0) {
  190.             fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
  191.             exit(1);
  192.         }
  193.         sin.sin_port = sp->s_port;
  194.         argc--, argv++;
  195.         if (argc > 0) {
  196.             sin.sin_port = atoi(*argv);
  197.             sin.sin_port = htons((u_short)sin.sin_port);
  198.         }
  199.  
  200.         s = socket(AF_INET, SOCK_STREAM, 0);
  201.         if (s < 0) {
  202.             perror("telnetd: socket");;
  203.             exit(1);
  204.         }
  205.         if (bind(s, &sin, sizeof sin) < 0) {
  206.         perror("bind");
  207.         exit(1);
  208.         }
  209.         if (listen(s, 1) < 0) {
  210.         perror("listen");
  211.         exit(1);
  212.         }
  213.         foo = sizeof sin;
  214.         ns = accept(s, &sin, &foo);
  215.         if (ns < 0) {
  216.         perror("accept");
  217.         exit(1);
  218.         }
  219.         dup2(ns, 0);
  220.         close(s);
  221.     }
  222. #endif    /* defined(DEBUG) */
  223.     openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
  224.     fromlen = sizeof (from);
  225.     if (getpeername(0, &from, &fromlen) < 0) {
  226.         fprintf(stderr, "%s: ", argv[0]);
  227.         perror("getpeername");
  228.         _exit(1);
  229.     }
  230.     if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
  231.         syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
  232.     }
  233.     doit(0, &from);
  234. }
  235.  
  236. char    *terminaltype = 0;
  237. #ifndef SYSV_LOGIN
  238. char    *envinit[2];
  239. #endif
  240. int    cleanup();
  241.  
  242. /*
  243.  * ttloop
  244.  *
  245.  *    A small subroutine to flush the network output buffer, get some data
  246.  * from the network, and pass it through the telnet state machine.  We
  247.  * also flush the pty input buffer (by dropping its data) if it becomes
  248.  * too full.
  249.  */
  250.  
  251. void
  252. ttloop()
  253. {
  254.     if (nfrontp-nbackp) {
  255.     netflush();
  256.     }
  257.     ncc = read(net, netibuf, sizeof netibuf);
  258.     if (ncc < 0) {
  259.     syslog(LOG_INFO, "ttloop:  read: %m\n");
  260.     exit(1);
  261.     } else if (ncc == 0) {
  262.     syslog(LOG_INFO, "ttloop:  peer died: %m\n");
  263.     exit(1);
  264.     }
  265.     netip = netibuf;
  266.     telrcv();            /* state machine */
  267.     if (ncc > 0) {
  268.     pfrontp = pbackp = ptyobuf;
  269.     telrcv();
  270.     }
  271. }
  272.  
  273. /*
  274.  * getterminaltype
  275.  *
  276.  *    Ask the other end to send along its terminal type.
  277.  * Output is the variable terminaltype filled in.
  278.  */
  279.  
  280. void
  281. getterminaltype()
  282. {
  283.     static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
  284.  
  285.     settimer(getterminal);
  286.     bcopy(sbuf, nfrontp, sizeof sbuf);
  287.     nfrontp += sizeof sbuf;
  288.     hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
  289.     while (sequenceIs(ttypeopt, getterminal)) {
  290.     ttloop();
  291.     }
  292.     if (hisopts[TELOPT_TTYPE] == OPT_YES) {
  293.     static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
  294.  
  295.     bcopy(sbbuf, nfrontp, sizeof sbbuf);
  296.     nfrontp += sizeof sbbuf;
  297.     while (sequenceIs(ttypesubopt, getterminal)) {
  298.         ttloop();
  299.     }
  300.     }
  301. }
  302.  
  303. /*
  304.  * Get a pty, scan input lines.
  305.  */
  306. doit(f, who)
  307.     int f;
  308.     struct sockaddr_in *who;
  309. {
  310.     char *host, *inet_ntoa();
  311.     int i, p, t;
  312.     struct sgttyb b;
  313.     struct hostent *hp;
  314. #ifdef STREAM_PTY
  315.     if ((p = open("/dev/ptmx", O_RDWR)) < 0)
  316.         fatal(f, "All network ports in use");
  317.     if (grantpt(p) < 0 || unlockpt(p) < 0)
  318.         fatal(f, "Cannot initialize pty slave");
  319.     dup2(f, 0);
  320.     if ((line = ptsname(p)) == 0 || (t = open(line, O_RDWR)) < 0)
  321.         fatal(f, "Cannot find  pty slave");
  322.     if (ioctl(t, I_PUSH, "ptem") < 0 || ioctl(t, I_PUSH, "ldterm") < 0 
  323.     || ioctl(t, I_PUSH, "ttcompat") < 0 || ioctl(p, I_PUSH, "pckt") < 0)
  324.         fatal(f, "Cannot push streams modules onto pty");
  325.     ioctl(t, TIOCGETP, &b);
  326.     /* XXX get and set seem to be sufficient with SunOS 5.0 */
  327.     ioctl(t, TIOCSETP, &b);
  328. #else /* STREAM_PTY */
  329.     int c;
  330.  
  331.     for (c = 'p'; c <= 's'; c++) {
  332.         struct stat stb;
  333.  
  334.         line = "/dev/ptyXX";
  335.         line[strlen("/dev/pty")] = c;
  336.         line[strlen("/dev/ptyp")] = '0';
  337.         if (stat(line, &stb) < 0)
  338.             break;
  339.         for (i = 0; i < 16; i++) {
  340.             line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
  341.             close(open(line, O_RDWR));
  342.             p = open(line, O_RDWR);
  343.             if (p > 0)
  344.                 goto gotpty;
  345.         }
  346.     }
  347.     fatal(f, "All network ports in use");
  348.     /*NOTREACHED*/
  349. gotpty:
  350.     dup2(f, 0);
  351.     line[strlen("/dev/")] = 't';
  352.     t = open("/dev/tty", O_RDWR);
  353.     if (t >= 0) {
  354.         ioctl(t, TIOCNOTTY, 0);
  355.         close(t);
  356.     }
  357.     t = open(line, O_RDWR);
  358.     if (t < 0)
  359.         fatalperror(f, line);
  360.     if (fchmod(t, 0))
  361.         fatalperror(f, line);
  362.     (void)signal(SIGHUP, SIG_IGN);
  363.     vhangup();
  364.     (void)signal(SIGHUP, SIG_DFL);
  365. #ifdef ultrix /* next open() should give us the ctty that vhangup() killed */
  366.     setpgrp(0, 0);
  367. #endif
  368.     t = open(line, O_RDWR);
  369.     if (t < 0)
  370.         fatalperror(f, line);
  371.     ioctl(t, TIOCGETP, &b);
  372.     b.sg_flags = CRMOD|XTABS|ANYP;
  373.     ioctl(t, TIOCSETP, &b);
  374.     ioctl(p, TIOCGETP, &b);
  375.     b.sg_flags &= ~ECHO;
  376.     ioctl(p, TIOCSETP, &b);
  377. #endif /* STREAM_PTY */
  378.     hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
  379.         who->sin_family);
  380.     if (hp)
  381.         host = hp->h_name;
  382.     else
  383.         host = inet_ntoa(who->sin_addr);
  384.  
  385.     net = f;
  386.     pty = p;
  387. #ifdef STREAM_PTY
  388.     pts = t;
  389. #endif
  390.  
  391.     /*
  392.      * get terminal type.
  393.      */
  394.     getterminaltype();
  395.  
  396.     if ((i = fork()) < 0)
  397.         fatalperror(f, "fork");
  398.     if (i)
  399.         telnet(f, p);
  400. #ifdef STREAM_PTY
  401.     /* Acquire a controlling terminal */
  402.     setsid();
  403.     i = t;
  404.     if ((t = open(line, O_RDWR)) < 0)
  405.         _exit(1);
  406.     close(i);
  407. #endif
  408.     close(f);
  409.     close(p);
  410.     dup2(t, 0);
  411.     dup2(t, 1);
  412.     dup2(t, 2);
  413.     close(t);
  414. #ifndef SYSV_LOGIN
  415.     envinit[0] = terminaltype;
  416.     envinit[1] = 0;
  417.     environ = envinit;
  418. #endif
  419.     /*
  420.      * -h : pass on name of host.
  421.      *        WARNING:  -h is accepted by login if and only if
  422.      *            getuid() == 0.
  423.      * -p : don't clobber the environment (so terminal type stays set).
  424.      */
  425. #ifdef SYSV_UTMP
  426.     /* SYSV login insists on an utmp(x) entry */
  427.     {
  428.         char *utmpx_ptsid(), *utmpx_id = utmpx_ptsid(line, "tn");
  429.         utmpx_init(line + sizeof("/dev/") - 1, ".telnet", utmpx_id);
  430.     }
  431. #endif
  432. #ifdef SYSV_LOGIN
  433.     execl("/bin/login", "login", "-h", host,
  434.                     terminaltype, (char *) 0);
  435. #else
  436.     execl("/bin/login", "login", "-h", host,
  437.                     terminaltype ? "-p" : 0, 0);
  438. #endif
  439.     syslog(LOG_ERR, "/bin/login: %m\n");
  440.     fatalperror(2, "/bin/login");
  441.     /*NOTREACHED*/
  442. }
  443.  
  444. fatal(f, msg)
  445.     int f;
  446.     char *msg;
  447. {
  448.     char buf[BUFSIZ];
  449.  
  450.     (void) sprintf(buf, "telnetd: %s.\r\n", msg);
  451.     (void) write(f, buf, strlen(buf));
  452.     exit(1);
  453. }
  454.  
  455. fatalperror(f, msg)
  456.     int f;
  457.     char *msg;
  458. {
  459.     char buf[BUFSIZ];
  460.     extern char *sys_errlist[];
  461.  
  462.     (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
  463.     fatal(f, buf);
  464. }
  465.  
  466.  
  467. /*
  468.  * Check a descriptor to see if out of band data exists on it.
  469.  */
  470.  
  471.  
  472. stilloob(s)
  473. int    s;        /* socket number */
  474. {
  475.     static struct timeval timeout = { 0 };
  476.     fd_set    excepts;
  477.     int value;
  478.  
  479.     do {
  480.     FD_ZERO(&excepts);
  481.     FD_SET(s, &excepts);
  482.     value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
  483.     } while ((value == -1) && (errno == EINTR));
  484.  
  485.     if (value < 0) {
  486.     fatalperror(pty, "select");
  487.     }
  488.     if (FD_ISSET(s, &excepts)) {
  489.     return 1;
  490.     } else {
  491.     return 0;
  492.     }
  493. }
  494.  
  495. /*
  496.  * Main loop.  Select from pty and network, and
  497.  * hand data to telnet receiver finite state machine.
  498.  */
  499. telnet(f, p)
  500. {
  501.     int on = 1;
  502.     char hostname[MAXHOSTNAMELEN];
  503. #define    TABBUFSIZ    512
  504.     char    defent[TABBUFSIZ];
  505.     char    defstrs[TABBUFSIZ];
  506. #undef    TABBUFSIZ
  507.     char *HE;
  508.     char *HN;
  509.     char *IM;
  510.  
  511.     ioctl(f, FIONBIO, &on);
  512.     ioctl(p, FIONBIO, &on);
  513. #ifndef STREAM_PTY
  514.     ioctl(p, TIOCPKT, &on);
  515. #endif
  516. #if    defined(SO_OOBINLINE)
  517.     setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
  518. #endif    /* defined(SO_OOBINLINE) */
  519.     signal(SIGTSTP, SIG_IGN);
  520.     /*
  521.      * Ignoring SIGTTOU keeps the kernel from blocking us
  522.      * in ttioctl() in /sys/tty.c.
  523.      */
  524.     signal(SIGTTOU, SIG_IGN);
  525.     signal(SIGCHLD, cleanup);
  526.     setpgrp(0, 0);
  527.  
  528.     /*
  529.      * Request to do remote echo and to suppress go ahead.
  530.      */
  531.     if (!myopts[TELOPT_ECHO]) {
  532.         dooption(TELOPT_ECHO);
  533.     }
  534.     if (!myopts[TELOPT_SGA]) {
  535.         dooption(TELOPT_SGA);
  536.     }
  537.     /*
  538.      * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
  539.      * because 4.2 clients are unable to deal with TCP urgent data.
  540.      *
  541.      * To find out, we send out a "DO ECHO".  If the remote system
  542.      * answers "WILL ECHO" it is probably a 4.2 client, and we note
  543.      * that fact ("WILL ECHO" ==> that the client will echo what
  544.      * WE, the server, sends it; it does NOT mean that the client will
  545.      * echo the terminal input).
  546.      */
  547.     (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
  548.     nfrontp += sizeof doopt-2;
  549.     hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
  550.  
  551.     /*
  552.      * Show banner that getty never gave.
  553.      *
  554.      * We put the banner in the pty input buffer.  This way, it
  555.      * gets carriage return null processing, etc., just like all
  556.      * other pty --> client data.
  557.      */
  558.  
  559.     gethostname(hostname, sizeof (hostname));
  560. #if 0
  561.     if (getent(defent, "default") == 1) {
  562.         char *getstr();
  563.         char *p=defstrs;
  564.  
  565.         HE = getstr("he", &p);
  566.         HN = getstr("hn", &p);
  567.         IM = getstr("im", &p);
  568.         if (HN && *HN)
  569.             strcpy(hostname, HN);
  570.         edithost(HE, hostname);
  571.         if (IM && *IM)
  572.             putf(IM, ptyibuf+1);
  573.     } else {
  574. #endif
  575.         sprintf(ptyibuf+1, BANNER, hostname);
  576. #if 0
  577.     }
  578. #endif
  579.  
  580.     ptyip = ptyibuf+1;        /* Prime the pump */
  581.     pcc = strlen(ptyip);        /* ditto */
  582.  
  583.     /* Clear ptybuf[0] - where the packet information is received */
  584.     ptyibuf[0] = 0;
  585.  
  586.     /*
  587.      * Call telrcv() once to pick up anything received during
  588.      * terminal type negotiation.
  589.      */
  590.     telrcv();
  591.  
  592.     for (;;) {
  593.         fd_set ibits, obits, xbits;
  594.         register int c;
  595.  
  596.         if (ncc < 0 && pcc < 0)
  597.             break;
  598.  
  599.         FD_ZERO(&ibits);
  600.         FD_ZERO(&obits);
  601.         FD_ZERO(&xbits);
  602.         /*
  603.          * Never look for input if there's still
  604.          * stuff in the corresponding output buffer
  605.          */
  606.         if (nfrontp - nbackp || pcc > 0) {
  607.             FD_SET(f, &obits);
  608.             FD_SET(p, &xbits);
  609.         } else {
  610.             FD_SET(p, &ibits);
  611.         }
  612.         if (pfrontp - pbackp || ncc > 0) {
  613.             FD_SET(p, &obits);
  614.         } else {
  615.             FD_SET(f, &ibits);
  616.         }
  617.         if (!SYNCHing) {
  618.             FD_SET(f, &xbits);
  619.         }
  620.         if ((c = select(16, &ibits, &obits, &xbits,
  621.                         (struct timeval *)0)) < 1) {
  622.             if (c == -1) {
  623.                 if (errno == EINTR) {
  624.                     continue;
  625.                 }
  626.             }
  627.             sleep(5);
  628.             continue;
  629.         }
  630.  
  631.         /*
  632.          * Any urgent data?
  633.          */
  634.         if (FD_ISSET(net, &xbits)) {
  635.             SYNCHing = 1;
  636.         }
  637.  
  638.         /*
  639.          * Something to read from the network...
  640.          */
  641.         if (FD_ISSET(net, &ibits)) {
  642. #if    !defined(SO_OOBINLINE)
  643.             /*
  644.              * In 4.2 (and 4.3 beta) systems, the
  645.              * OOB indication and data handling in the kernel
  646.              * is such that if two separate TCP Urgent requests
  647.              * come in, one byte of TCP data will be overlaid.
  648.              * This is fatal for Telnet, but we try to live
  649.              * with it.
  650.              *
  651.              * In addition, in 4.2 (and...), a special protocol
  652.              * is needed to pick up the TCP Urgent data in
  653.              * the correct sequence.
  654.              *
  655.              * What we do is:  if we think we are in urgent
  656.              * mode, we look to see if we are "at the mark".
  657.              * If we are, we do an OOB receive.  If we run
  658.              * this twice, we will do the OOB receive twice,
  659.              * but the second will fail, since the second
  660.              * time we were "at the mark", but there wasn't
  661.              * any data there (the kernel doesn't reset
  662.              * "at the mark" until we do a normal read).
  663.              * Once we've read the OOB data, we go ahead
  664.              * and do normal reads.
  665.              *
  666.              * There is also another problem, which is that
  667.              * since the OOB byte we read doesn't put us
  668.              * out of OOB state, and since that byte is most
  669.              * likely the TELNET DM (data mark), we would
  670.              * stay in the TELNET SYNCH (SYNCHing) state.
  671.              * So, clocks to the rescue.  If we've "just"
  672.              * received a DM, then we test for the
  673.              * presence of OOB data when the receive OOB
  674.              * fails (and AFTER we did the normal mode read
  675.              * to clear "at the mark").
  676.              */
  677.             if (SYNCHing) {
  678.             int atmark;
  679.  
  680.             ioctl(net, SIOCATMARK, (char *)&atmark);
  681.             if (atmark) {
  682.                 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
  683.                 if ((ncc == -1) && (errno == EINVAL)) {
  684.                 ncc = read(net, netibuf, sizeof (netibuf));
  685.                 if (sequenceIs(didnetreceive, gotDM)) {
  686.                     SYNCHing = stilloob(net);
  687.                 }
  688.                 }
  689.             } else {
  690.                 ncc = read(net, netibuf, sizeof (netibuf));
  691.             }
  692.             } else {
  693.             ncc = read(net, netibuf, sizeof (netibuf));
  694.             }
  695.             settimer(didnetreceive);
  696. #else    /* !defined(SO_OOBINLINE)) */
  697.             ncc = read(net, netibuf, sizeof (netibuf));
  698. #endif    /* !defined(SO_OOBINLINE)) */
  699.             if (ncc < 0 && errno == EWOULDBLOCK)
  700.             ncc = 0;
  701.             else {
  702.             if (ncc <= 0) {
  703.                 break;
  704.             }
  705.             netip = netibuf;
  706.             }
  707.         }
  708.  
  709.         /*
  710.          * Something to read from the pty...
  711.          */
  712.         if (FD_ISSET(p, &xbits)) {
  713.             if (PTY_PKT_READ(p, ptyibuf, 1) != 1) {
  714.                 break;
  715.             }
  716.         }
  717.         if (FD_ISSET(p, &ibits)) {
  718.             pcc = PTY_PKT_READ(p, ptyibuf, BUFSIZ);
  719.             if (pcc < 0 && errno == EWOULDBLOCK)
  720.                 pcc = 0;
  721.             else {
  722.                 if (pcc <= 0)
  723.                     break;
  724.                 /* Skip past "packet" */
  725.                 pcc--;
  726.                 ptyip = ptyibuf+1;
  727.             }
  728.         }
  729.         if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
  730.             netclear();    /* clear buffer back */
  731.             *nfrontp++ = IAC;
  732.             *nfrontp++ = DM;
  733.             neturg = nfrontp-1;  /* off by one XXX */
  734.             ptyibuf[0] = 0;
  735.         }
  736.  
  737.         while (pcc > 0) {
  738.             if ((&netobuf[BUFSIZ] - nfrontp) < 2)
  739.                 break;
  740.             c = *ptyip++ & 0377, pcc--;
  741.             if (c == IAC)
  742.                 *nfrontp++ = c;
  743.             *nfrontp++ = c;
  744.             /* Don't do CR-NUL if we are in binary mode */
  745.             if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
  746.                 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
  747.                     *nfrontp++ = *ptyip++ & 0377;
  748.                     pcc--;
  749.                 } else
  750.                     *nfrontp++ = '\0';
  751.             }
  752.         }
  753.         if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
  754.             netflush();
  755.         if (ncc > 0)
  756.             telrcv();
  757.         if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
  758.             ptyflush();
  759.     }
  760.     cleanup();
  761. }
  762.     
  763. /*
  764.  * State for recv fsm
  765.  */
  766. #define    TS_DATA        0    /* base state */
  767. #define    TS_IAC        1    /* look for double IAC's */
  768. #define    TS_CR        2    /* CR-LF ->'s CR */
  769. #define    TS_SB        3    /* throw away begin's... */
  770. #define    TS_SE        4    /* ...end's (suboption negotiation) */
  771. #define    TS_WILL        5    /* will option negotiation */
  772. #define    TS_WONT        6    /* wont " */
  773. #define    TS_DO        7    /* do " */
  774. #define    TS_DONT        8    /* dont " */
  775.  
  776. telrcv()
  777. {
  778.     register int c;
  779.     static int state = TS_DATA;
  780. #ifdef STREAM_PTY
  781.     int pty = pts;    /* XXX apply ioctl()s at slave end */
  782. #endif
  783.  
  784.     while (ncc > 0) {
  785.         if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
  786.             return;
  787.         c = *netip++ & 0377, ncc--;
  788.         switch (state) {
  789.  
  790.         case TS_CR:
  791.             state = TS_DATA;
  792.             /* Strip off \n or \0 after a \r */
  793.             if ((c == 0) || (c == '\n')) {
  794.                 break;
  795.             }
  796.             /* FALL THROUGH */
  797.  
  798.         case TS_DATA:
  799.             if (c == IAC) {
  800.                 state = TS_IAC;
  801.                 break;
  802.             }
  803.             if (inter > 0)
  804.                 break;
  805.             /*
  806.              * We now map \r\n ==> \r for pragmatic reasons.
  807.              * Many client implementations send \r\n when
  808.              * the user hits the CarriageReturn key.
  809.              *
  810.              * We USED to map \r\n ==> \n, since \r\n says
  811.              * that we want to be in column 1 of the next
  812.              * printable line, and \n is the standard
  813.              * unix way of saying that (\r is only good
  814.              * if CRMOD is set, which it normally is).
  815.              */
  816.             if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
  817.                 state = TS_CR;
  818.             }
  819.             *pfrontp++ = c;
  820.             break;
  821.  
  822.         case TS_IAC:
  823.             switch (c) {
  824.  
  825.             /*
  826.              * Send the process on the pty side an
  827.              * interrupt.  Do this with a NULL or
  828.              * interrupt char; depending on the tty mode.
  829.              */
  830.             case IP:
  831.                 interrupt();
  832.                 break;
  833.  
  834.             case BREAK:
  835.                 sendbrk();
  836.                 break;
  837.  
  838.             /*
  839.              * Are You There?
  840.              */
  841.             case AYT:
  842.                 strcpy(nfrontp, "\r\n[Yes]\r\n");
  843.                 nfrontp += 9;
  844.                 break;
  845.  
  846.             /*
  847.              * Abort Output
  848.              */
  849.             case AO: {
  850.                     struct ltchars tmpltc;
  851.  
  852.                     ptyflush();    /* half-hearted */
  853.                     ioctl(pty, TIOCGLTC, &tmpltc);
  854.                     if (tmpltc.t_flushc != '\377') {
  855.                         *pfrontp++ = tmpltc.t_flushc;
  856.                     }
  857.                     netclear();    /* clear buffer back */
  858.                     *nfrontp++ = IAC;
  859.                     *nfrontp++ = DM;
  860.                     neturg = nfrontp-1; /* off by one XXX */
  861.                     break;
  862.                 }
  863.  
  864.             /*
  865.              * Erase Character and
  866.              * Erase Line
  867.              */
  868.             case EC:
  869.             case EL: {
  870.                     struct sgttyb b;
  871.                     char ch;
  872.  
  873.                     ptyflush();    /* half-hearted */
  874.                     ioctl(pty, TIOCGETP, &b);
  875.                     ch = (c == EC) ?
  876.                         b.sg_erase : b.sg_kill;
  877.                     if (ch != '\377') {
  878.                         *pfrontp++ = ch;
  879.                     }
  880.                     break;
  881.                 }
  882.  
  883.             /*
  884.              * Check for urgent data...
  885.              */
  886.             case DM:
  887.                 SYNCHing = stilloob(net);
  888.                 settimer(gotDM);
  889.                 break;
  890.  
  891.  
  892.             /*
  893.              * Begin option subnegotiation...
  894.              */
  895.             case SB:
  896.                 state = TS_SB;
  897.                 continue;
  898.  
  899.             case WILL:
  900.                 state = TS_WILL;
  901.                 continue;
  902.  
  903.             case WONT:
  904.                 state = TS_WONT;
  905.                 continue;
  906.  
  907.             case DO:
  908.                 state = TS_DO;
  909.                 continue;
  910.  
  911.             case DONT:
  912.                 state = TS_DONT;
  913.                 continue;
  914.  
  915.             case IAC:
  916.                 *pfrontp++ = c;
  917.                 break;
  918.             }
  919.             state = TS_DATA;
  920.             break;
  921.  
  922.         case TS_SB:
  923.             if (c == IAC) {
  924.                 state = TS_SE;
  925.             } else {
  926.                 SB_ACCUM(c);
  927.             }
  928.             break;
  929.  
  930.         case TS_SE:
  931.             if (c != SE) {
  932.                 if (c != IAC) {
  933.                     SB_ACCUM(IAC);
  934.                 }
  935.                 SB_ACCUM(c);
  936.                 state = TS_SB;
  937.             } else {
  938.                 SB_TERM();
  939.                 suboption();    /* handle sub-option */
  940.                 state = TS_DATA;
  941.             }
  942.             break;
  943.  
  944.         case TS_WILL:
  945.             if (hisopts[c] != OPT_YES)
  946.                 willoption(c);
  947.             state = TS_DATA;
  948.             continue;
  949.  
  950.         case TS_WONT:
  951.             if (hisopts[c] != OPT_NO)
  952.                 wontoption(c);
  953.             state = TS_DATA;
  954.             continue;
  955.  
  956.         case TS_DO:
  957.             if (myopts[c] != OPT_YES)
  958.                 dooption(c);
  959.             state = TS_DATA;
  960.             continue;
  961.  
  962.         case TS_DONT:
  963.             if (myopts[c] != OPT_NO) {
  964.                 dontoption(c);
  965.             }
  966.             state = TS_DATA;
  967.             continue;
  968.  
  969.         default:
  970.             syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
  971.             printf("telnetd: panic state=%d\n", state);
  972.             exit(1);
  973.         }
  974.     }
  975. }
  976.  
  977. willoption(option)
  978.     int option;
  979. {
  980.     char *fmt;
  981.  
  982.     switch (option) {
  983.  
  984.     case TELOPT_BINARY:
  985.         mode(RAW, 0);
  986.         fmt = doopt;
  987.         break;
  988.  
  989.     case TELOPT_ECHO:
  990.         not42 = 0;        /* looks like a 4.2 system */
  991.         /*
  992.          * Now, in a 4.2 system, to break them out of ECHOing
  993.          * (to the terminal) mode, we need to send a "WILL ECHO".
  994.          * Kludge upon kludge!
  995.          */
  996.         if (myopts[TELOPT_ECHO] == OPT_YES) {
  997.             dooption(TELOPT_ECHO);
  998.         }
  999.         fmt = dont;
  1000.         break;
  1001.  
  1002.     case TELOPT_TTYPE:
  1003.         settimer(ttypeopt);
  1004.         if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
  1005.             hisopts[TELOPT_TTYPE] = OPT_YES;
  1006.             return;
  1007.         }
  1008.         fmt = doopt;
  1009.         break;
  1010.  
  1011.     case TELOPT_SGA:
  1012.         fmt = doopt;
  1013.         break;
  1014.  
  1015.     case TELOPT_TM:
  1016.         fmt = dont;
  1017.         break;
  1018.  
  1019.     default:
  1020.         fmt = dont;
  1021.         break;
  1022.     }
  1023.     if (fmt == doopt) {
  1024.         hisopts[option] = OPT_YES;
  1025.     } else {
  1026.         hisopts[option] = OPT_NO;
  1027.     }
  1028.     (void) sprintf(nfrontp, fmt, option);
  1029.     nfrontp += sizeof (dont) - 2;
  1030. }
  1031.  
  1032. wontoption(option)
  1033.     int option;
  1034. {
  1035.     char *fmt;
  1036.  
  1037.     switch (option) {
  1038.     case TELOPT_ECHO:
  1039.         not42 = 1;        /* doesn't seem to be a 4.2 system */
  1040.         break;
  1041.  
  1042.     case TELOPT_BINARY:
  1043.         mode(0, RAW);
  1044.         break;
  1045.  
  1046.     case TELOPT_TTYPE:
  1047.         settimer(ttypeopt);
  1048.         break;
  1049.     }
  1050.  
  1051.     fmt = dont;
  1052.     hisopts[option] = OPT_NO;
  1053.     (void) sprintf(nfrontp, fmt, option);
  1054.     nfrontp += sizeof (doopt) - 2;
  1055. }
  1056.  
  1057. dooption(option)
  1058.     int option;
  1059. {
  1060.     char *fmt;
  1061.  
  1062.     switch (option) {
  1063.  
  1064.     case TELOPT_TM:
  1065.         fmt = wont;
  1066.         break;
  1067.  
  1068.     case TELOPT_ECHO:
  1069.         mode(ECHO|CRMOD, 0);
  1070.         fmt = will;
  1071.         break;
  1072.  
  1073.     case TELOPT_BINARY:
  1074.         mode(RAW, 0);
  1075.         fmt = will;
  1076.         break;
  1077.  
  1078.     case TELOPT_SGA:
  1079.         fmt = will;
  1080.         break;
  1081.  
  1082.     default:
  1083.         fmt = wont;
  1084.         break;
  1085.     }
  1086.     if (fmt == will) {
  1087.         myopts[option] = OPT_YES;
  1088.     } else {
  1089.         myopts[option] = OPT_NO;
  1090.     }
  1091.     (void) sprintf(nfrontp, fmt, option);
  1092.     nfrontp += sizeof (doopt) - 2;
  1093. }
  1094.  
  1095.  
  1096. dontoption(option)
  1097. int option;
  1098. {
  1099.     char *fmt;
  1100.  
  1101.     switch (option) {
  1102.     case TELOPT_ECHO:        /* we should stop echoing */
  1103.     mode(0, ECHO);
  1104.     fmt = wont;
  1105.     break;
  1106.  
  1107.     default:
  1108.     fmt = wont;
  1109.     break;
  1110.     }
  1111.  
  1112.     if (fmt = wont) {
  1113.     myopts[option] = OPT_NO;
  1114.     } else {
  1115.     myopts[option] = OPT_YES;
  1116.     }
  1117.     (void) sprintf(nfrontp, fmt, option);
  1118.     nfrontp += sizeof (wont) - 2;
  1119. }
  1120.  
  1121. /*
  1122.  * suboption()
  1123.  *
  1124.  *    Look at the sub-option buffer, and try to be helpful to the other
  1125.  * side.
  1126.  *
  1127.  *    Currently we recognize:
  1128.  *
  1129.  *    Terminal type is
  1130.  */
  1131.  
  1132. suboption()
  1133. {
  1134.     switch (SB_GET()) {
  1135.     case TELOPT_TTYPE: {        /* Yaaaay! */
  1136.     static char terminalname[5+41] = "TERM=";
  1137.  
  1138.     settimer(ttypesubopt);
  1139.  
  1140.     if (SB_GET() != TELQUAL_IS) {
  1141.         return;        /* ??? XXX but, this is the most robust */
  1142.     }
  1143.  
  1144.     terminaltype = terminalname+strlen(terminalname);
  1145.  
  1146.     while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
  1147.                                     !SB_EOF()) {
  1148.         register int c;
  1149.  
  1150.         c = SB_GET();
  1151.         if (isupper(c)) {
  1152.         c = tolower(c);
  1153.         }
  1154.         *terminaltype++ = c;    /* accumulate name */
  1155.     }
  1156.     *terminaltype = 0;
  1157.     terminaltype = terminalname;
  1158.     break;
  1159.     }
  1160.  
  1161.     default:
  1162.     ;
  1163.     }
  1164. }
  1165.  
  1166. mode(on, off)
  1167.     int on, off;
  1168. {
  1169.     struct sgttyb b;
  1170. #ifdef STREAM_PTY
  1171.     int pty = pts;    /* XXX apply ioctl()s at slave end */
  1172. #endif
  1173.  
  1174.     ptyflush();
  1175.     ioctl(pty, TIOCGETP, &b);
  1176.     b.sg_flags |= on;
  1177.     b.sg_flags &= ~off;
  1178.     ioctl(pty, TIOCSETP, &b);
  1179. }
  1180.  
  1181. /*
  1182.  * Send interrupt to process on other side of pty.
  1183.  * If it is in raw mode, just write NULL;
  1184.  * otherwise, write intr char.
  1185.  */
  1186. interrupt()
  1187. {
  1188.     struct sgttyb b;
  1189.     struct tchars tchars;
  1190. #ifdef STREAM_PTY
  1191.     int pty = pts;    /* XXX apply ioctl()s at slave end */
  1192. #endif
  1193.  
  1194.     ptyflush();    /* half-hearted */
  1195.     ioctl(pty, TIOCGETP, &b);
  1196.     if (b.sg_flags & RAW) {
  1197.         *pfrontp++ = '\0';
  1198.         return;
  1199.     }
  1200.     *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
  1201.         '\177' : tchars.t_intrc;
  1202. }
  1203.  
  1204. /*
  1205.  * Send quit to process on other side of pty.
  1206.  * If it is in raw mode, just write NULL;
  1207.  * otherwise, write quit char.
  1208.  */
  1209. sendbrk()
  1210. {
  1211.     struct sgttyb b;
  1212.     struct tchars tchars;
  1213. #ifdef STREAM_PTY
  1214.     int pty = pts;    /* XXX apply ioctl()s at slave end */
  1215. #endif
  1216.  
  1217.     ptyflush();    /* half-hearted */
  1218.     ioctl(pty, TIOCGETP, &b);
  1219.     if (b.sg_flags & RAW) {
  1220.         *pfrontp++ = '\0';
  1221.         return;
  1222.     }
  1223.     *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
  1224.         '\034' : tchars.t_quitc;
  1225. }
  1226.  
  1227. ptyflush()
  1228. {
  1229.     int n;
  1230.  
  1231.     if ((n = pfrontp - pbackp) > 0)
  1232.         n = write(pty, pbackp, n);
  1233.     if (n < 0)
  1234.         return;
  1235.     pbackp += n;
  1236.     if (pbackp == pfrontp)
  1237.         pbackp = pfrontp = ptyobuf;
  1238. }
  1239.  
  1240. /*
  1241.  * nextitem()
  1242.  *
  1243.  *    Return the address of the next "item" in the TELNET data
  1244.  * stream.  This will be the address of the next character if
  1245.  * the current address is a user data character, or it will
  1246.  * be the address of the character following the TELNET command
  1247.  * if the current address is a TELNET IAC ("I Am a Command")
  1248.  * character.
  1249.  */
  1250.  
  1251. char *
  1252. nextitem(current)
  1253. char    *current;
  1254. {
  1255.     if ((*current&0xff) != IAC) {
  1256.     return current+1;
  1257.     }
  1258.     switch (*(current+1)&0xff) {
  1259.     case DO:
  1260.     case DONT:
  1261.     case WILL:
  1262.     case WONT:
  1263.     return current+3;
  1264.     case SB:        /* loop forever looking for the SE */
  1265.     {
  1266.         register char *look = current+2;
  1267.  
  1268.         for (;;) {
  1269.         if ((*look++&0xff) == IAC) {
  1270.             if ((*look++&0xff) == SE) {
  1271.             return look;
  1272.             }
  1273.         }
  1274.         }
  1275.     }
  1276.     default:
  1277.     return current+2;
  1278.     }
  1279. }
  1280.  
  1281.  
  1282. /*
  1283.  * netclear()
  1284.  *
  1285.  *    We are about to do a TELNET SYNCH operation.  Clear
  1286.  * the path to the network.
  1287.  *
  1288.  *    Things are a bit tricky since we may have sent the first
  1289.  * byte or so of a previous TELNET command into the network.
  1290.  * So, we have to scan the network buffer from the beginning
  1291.  * until we are up to where we want to be.
  1292.  *
  1293.  *    A side effect of what we do, just to keep things
  1294.  * simple, is to clear the urgent data pointer.  The principal
  1295.  * caller should be setting the urgent data pointer AFTER calling
  1296.  * us in any case.
  1297.  */
  1298.  
  1299. netclear()
  1300. {
  1301.     register char *thisitem, *next;
  1302.     char *good;
  1303. #define    wewant(p)    ((nfrontp > p) && ((*p&0xff) == IAC) && \
  1304.                 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
  1305.  
  1306.     thisitem = netobuf;
  1307.  
  1308.     while ((next = nextitem(thisitem)) <= nbackp) {
  1309.     thisitem = next;
  1310.     }
  1311.  
  1312.     /* Now, thisitem is first before/at boundary. */
  1313.  
  1314.     good = netobuf;    /* where the good bytes go */
  1315.  
  1316.     while (nfrontp > thisitem) {
  1317.     if (wewant(thisitem)) {
  1318.         int length;
  1319.  
  1320.         next = thisitem;
  1321.         do {
  1322.         next = nextitem(next);
  1323.         } while (wewant(next) && (nfrontp > next));
  1324.         length = next-thisitem;
  1325.         bcopy(thisitem, good, length);
  1326.         good += length;
  1327.         thisitem = next;
  1328.     } else {
  1329.         thisitem = nextitem(thisitem);
  1330.     }
  1331.     }
  1332.  
  1333.     nbackp = netobuf;
  1334.     nfrontp = good;        /* next byte to be sent */
  1335.     neturg = 0;
  1336. }
  1337.  
  1338. /*
  1339.  *  netflush
  1340.  *        Send as much data as possible to the network,
  1341.  *    handling requests for urgent data.
  1342.  */
  1343.  
  1344.  
  1345. netflush()
  1346. {
  1347.     int n;
  1348.  
  1349.     if ((n = nfrontp - nbackp) > 0) {
  1350.     /*
  1351.      * if no urgent data, or if the other side appears to be an
  1352.      * old 4.2 client (and thus unable to survive TCP urgent data),
  1353.      * write the entire buffer in non-OOB mode.
  1354.      */
  1355.     if ((neturg == 0) || (not42 == 0)) {
  1356.         n = write(net, nbackp, n);    /* normal write */
  1357.     } else {
  1358.         n = neturg - nbackp;
  1359.         /*
  1360.          * In 4.2 (and 4.3) systems, there is some question about
  1361.          * what byte in a sendOOB operation is the "OOB" data.
  1362.          * To make ourselves compatible, we only send ONE byte
  1363.          * out of band, the one WE THINK should be OOB (though
  1364.          * we really have more the TCP philosophy of urgent data
  1365.          * rather than the Unix philosophy of OOB data).
  1366.          */
  1367.         if (n > 1) {
  1368.         n = send(net, nbackp, n-1, 0);    /* send URGENT all by itself */
  1369.         } else {
  1370.         n = send(net, nbackp, n, MSG_OOB);    /* URGENT data */
  1371.         }
  1372.     }
  1373.     }
  1374.     if (n < 0) {
  1375.     if (errno == EWOULDBLOCK)
  1376.         return;
  1377.     /* should blow this guy away... */
  1378.     return;
  1379.     }
  1380.     nbackp += n;
  1381.     if (nbackp >= neturg) {
  1382.     neturg = 0;
  1383.     }
  1384.     if (nbackp == nfrontp) {
  1385.     nbackp = nfrontp = netobuf;
  1386.     }
  1387. }
  1388.  
  1389. cleanup()
  1390. {
  1391.     char *p;
  1392.  
  1393.     p = line + sizeof("/dev/") - 1;
  1394. #ifdef SYSV_UTMP
  1395.     utmpx_logout(p);
  1396.     (void)chown(line, 0, 0);
  1397.     (void)chmod(line, 0644);
  1398. #else /* SYSV_UTMP */
  1399.     if (logout(p))
  1400.         logwtmp(p, "", "");
  1401.     (void)chmod(line, 0666);
  1402.     (void)chown(line, 0, 0);
  1403.     *p = 'p';
  1404.     (void)chmod(line, 0666);
  1405.     (void)chown(line, 0, 0);
  1406. #endif /* SYSV_UTMP */
  1407.     shutdown(net, 2);
  1408.     exit(1);
  1409. }
  1410.  
  1411. char    editedhost[32];
  1412.  
  1413. edithost(pat, host)
  1414.     register char *pat;
  1415.     register char *host;
  1416. {
  1417.     register char *res = editedhost;
  1418.  
  1419.     if (!pat)
  1420.         pat = "";
  1421.     while (*pat) {
  1422.         switch (*pat) {
  1423.  
  1424.         case '#':
  1425.             if (*host)
  1426.                 host++;
  1427.             break;
  1428.  
  1429.         case '@':
  1430.             if (*host)
  1431.                 *res++ = *host++;
  1432.             break;
  1433.  
  1434.         default:
  1435.             *res++ = *pat;
  1436.             break;
  1437.  
  1438.         }
  1439.         if (res == &editedhost[sizeof editedhost - 1]) {
  1440.             *res = '\0';
  1441.             return;
  1442.         }
  1443.         pat++;
  1444.     }
  1445.     if (*host)
  1446.         strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
  1447.     else
  1448.         *res = '\0';
  1449.     editedhost[sizeof editedhost - 1] = '\0';
  1450. }
  1451.  
  1452. static char *putlocation;
  1453.  
  1454. puts(s)
  1455. register char *s;
  1456. {
  1457.  
  1458.     while (*s)
  1459.         putchr(*s++);
  1460. }
  1461.  
  1462. putchr(cc)
  1463. {
  1464.     *putlocation++ = cc;
  1465. }
  1466.  
  1467. putf(cp, where)
  1468. register char *cp;
  1469. char *where;
  1470. {
  1471.     char *slash;
  1472.     char datebuffer[60];
  1473.     extern char *rindex();
  1474.  
  1475.     putlocation = where;
  1476.  
  1477.     while (*cp) {
  1478.         if (*cp != '%') {
  1479.             putchr(*cp++);
  1480.             continue;
  1481.         }
  1482.         switch (*++cp) {
  1483.  
  1484.         case 't':
  1485.             slash = rindex(line, '/');
  1486.             if (slash == (char *) 0)
  1487.                 puts(line);
  1488.             else
  1489.                 puts(&slash[1]);
  1490.             break;
  1491.  
  1492.         case 'h':
  1493.             puts(editedhost);
  1494.             break;
  1495.  
  1496.         case 'd':
  1497. #if 0
  1498.             get_date(datebuffer);
  1499.             puts(datebuffer);
  1500. #else
  1501.             puts("hi there");
  1502. #endif
  1503.             break;
  1504.  
  1505.         case '%':
  1506.             putchr('%');
  1507.             break;
  1508.         }
  1509.         cp++;
  1510.     }
  1511. }
  1512.